#※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
# File Name: file.sh
# Author: GID5564
# Description:
# Created Time: 07/11/23-19:37:27
#※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
#! /usr/bin/env bash


# 避免重复导入
#[[ -n $__XLIB_IMPORTED__FILE ]] && return 0
#__XLIB_IMPORTED__FILE=1

function __file_init__() {
    import backup
}




# 功能：验证文件的SHA256哈希值是否匹配预期值
# 使用方法：check_strict <文件路径> <预期SHA256哈希值>

function check_strict() {
    local file="$1"
    local expected_sha256="$2"

    # 检查是否提供了两个参数
    if [[ -z "$file" || -z "$expected_sha256" ]]; then
        echo "Usage: check_strict <file_path> <expected_sha256>" >&2
        return 1
    fi

    # 检查文件是否存在
    if [[ ! -f "$file" ]]; then
        echo "Error: File '$file' does not exist." >&2
        return 1
    fi

    # 验证SHA256哈希值
    sha256sum --check --strict --status <<< "$expected_sha256  $file"
    local result=$?

    # 根据sha256sum的返回值决定退出状态
    if [[ $result -eq 0 ]]; then
        echo "SHA256 check passed for '$file'."
        return 0
    elif [[ $result -eq 1 ]]; then
        echo "SHA256 check failed for '$file'." >&2
        return 1
    else
        echo "An error occurred during SHA256 verification." >&2
        return $result
    fi
}


# 文件数量
#file_count "/home/aidboot" 
#file_count "/home/aidboot" "*.txt"
#file_count "/home/aidboot" "*"
function file_count(){
    [[ -z "$1" ]] && {
        error "Usage: file_count <目录路径> [*|*.xxx]"
        return $INVALID_ARGUMENTS
    }
    [[ ! -d ${1%/*} ]] && {
        error "找不到文件或目录: ${1}"
        return $PATH_NOT_FOUND
    }
    local folder=$1
    local name=$2
    [[ -z "$2" ]] && {
        name='*'
    }
    
    local count=$(find $folder -name $name -type f | wc -l)
    echo $count
    unset folder
    unset name
}


#Executable

# 创建指定的文件
function new_file() {
    [[ -z $1 ]] && error "Usage: new_file <全路径文件>" && return $INVALID_ARGUMENTS
    local file="$1"
    local basename=$(basename $file)
    local parent_dir=$(dirname "$1")
    new_path $parent_dir && touch "$parent_dir/$basename"
    unset file
    unset basename
    unset basename
}

function write_to_file() {
    # 无需检查文件是否存在，直接写入
    echo "$1" >>$2
}


# 创建新目录
function new_path() {
    [[ -z $1 ]] && error "Usage: new_path <目录路径>" && return $INVALID_ARGUMENTS
    local path="$1"
    mkdir -p "$path" || return $FILE_CREATE_FAILURE
    unset path
}

# 获取指定文件的parent目录的绝对路径，即使文件不存在
function parent() {
    [[ -z "$1" ]] && echo $(pwd) && return 0
    
    local file="$1"
    echo $(dirname "$file")
    unset file
}


########################################
# 描述: 获取文件名后缀
# 参数: $1:文件名
# 返回: 文件名后缀 
# 实例: FileSuffix "xxxx.xxx"
########################################
function file_suffix() {
    [[ -z "$1" ]] && {
        error "Usage: file_suffix <file>"
        return $INVALID_ARGUMENTS
    }

    local filename="$1"
    if [ -n "$filename" ]; then
        echo "${filename##*.}"
    fi
    unset filename
}

########################################
# 描述: 获取文件名前缀
# 参数: $1:文件名
# 返回: 文件名前缀
# 实例: FilePrefix "xxxx.xxx"
########################################
function file_prefix() {
    [[ -z "$1" ]] && {
        error "Usage: file_prefix <file>"
        return $INVALID_ARGUMENTS
    }

    local filename="$1"
    if [ -n "$filename" ]; then
        echo "${filename%.*}"
    fi
    unset filename
}



# full_name <file>
# 文件的带绝对路径的name
function full_name() {
    [[ -z $1 ]] && error "Usage: full_name <文件>" && return $INVALID_ARGUMENTS

    echo $(absolute "$1")
}

# file_name <file or path>
# 文件的全名称: 名称+扩展名 aa/cc.log -> cc.log，不含路径
# https://www.cnblogs.com/yanwei-wang/p/8135489.html
function file_name() {
    [[ -z $1 ]] && error "Usage: file_name <file|path>" && return $INVALID_ARGUMENTS

    local file=$1
    # using echo to return values
    #  echo ${file%.*}
    echo "$(basename $file)"
    unset file
}

# base_name <file>
# 获取去掉最后一个ext后的文件名，不含路径
# aa/bb/cc.pom.xml -> cc.pom
function base_name() {
    [[ -z $1 ]] && error "Usage: base_name <目录路径>" && return $INVALID_ARGUMENTS

    local file="$1"
    local s=$(\basename $file)
    echo "${s%.*}"
    unset file
    unset s
}

# lastExt <file>
# 获取文件扩展名的最后一段
# a.b.c -> c
function last_ext() {
    [[ -z $1 ]] && error "Usage: last_ext <文件>" && return $INVALID_ARGUMENTS

    local file=$1
    echo "${file##*.}"
    return 1
}

# fullExt <file>
# 除文件名之外的所有ext的字段，不含路径
# a.b.c -> b.c
function full_ext() {
    [[ -z $1 ]] && error "Usage: full_ext <文件>" && return $INVALID_ARGUMENTS

    local file=$1
    echo "${file#*.}"
}

# 获取文件/目录的绝对路径
function absolute() {
    # cygpath 最简单
    [[ -z "$1" ]] && {
        error "Usage: absolute <file|path>"
        return $INVALID_ARGUMENTS
    }

    [[ -n $1 ]] && readlink -s "$1"
}

# 一次创建多个目录
function mkd() {
    [[ -z "$1" ]] && {
        error "Usage: mkd <目录列表>"
        return $INVALID_ARGUMENTS
    }

    local dir=$@
    local fails=0 failed_dirs=()
    for dir; do
        mkdir -p -- "$dir"
        if (($? != 0)); then
            ((fails++))
            failed_dirs+=("$dir")
        fi
    done
    if ((fails == 1)); then
        error "创建目录:  ${failed_dirs[0]} 失败!"
    elif ((fails > 1)); then
        error "创建目录: (${failed_dirs[@]}) 失败!"
    fi
    return 0
    unset dir
    unset fails
    unset failed_dirs
}



# extract <compressed-sile> [target directory]
#文件解压
function extract() {
    [[ -z "$1" ]] && {
        error "Usage: extract <file>"
        return $INVALID_ARGUMENTS
    }

    if [ -s $1 ]; then
        case $1 in
        *.tar.bz2) tar xjf $1 ;;
        *.tar.gz) tar xzf $1 ;;
        *.bz2) bunzip2 $1 ;;
        *.rar) unrar e $1 ;;
        *.gz) gunzip $1 ;;
        *.tar) tar xf $1 ;;
        *.tbz2) tar xjf $1 ;;
        *.tgz) tar xzf $1 ;;
        *.zip) unzip $1 ;;
        *.Z) uncompress $1 ;;
        *.7z) 7z x $1 ;;
        *) error "${1} 无法通过 extract() 函数解压!" ;;
        esac
    else
        error "${1} 是一个无效的文件!"
    fi
}

# targz <target file name> <file list>
#压缩文件
# package <file list> to a tar.gz
function targz() {
    [[ -z "$1" ]] && {
        error "Usage: targz <file list>"
        return $INVALID_ARGUMENTS
    }

    local target="$1"
    shift

    import string
    local tmpFile="$(rand 1 100).tar"

    # todo 需要一个默认排除文件的设置
    local _exclude=".git"
    tar -cvf "${tmpFile}" --exclude="$_exclude" "${@}" || return 1

    size=$(
        stat -s"%z" "${tmpFile}" 2>/dev/null # macOS `stat`
        stat -c"%s" "${tmpFile}" 2>/dev/null # GNU `stat`
    )

    local cmd=""
    if ((size < 52428800)) && hash zopfli 2>/dev/null; then
        # the .tar file is smaller than 50 MB and Zopfli is available; use it
        cmd="zopfli"
    else
        if hash pigz 2>/dev/null; then
            cmd="pigz"
        else
            cmd="gzip"
        fi
    fi

    echo "Compressing .tar ($((size / 1000)) kB) using \`${cmd}\`…"
    "${cmd}" -v "${tmpFile}" || return 1
    [[ -s "${tmpFile}" ]] && rm "${tmpFile}"

    zippedSize=$(
        stat -s"%z" "${tmpFile}.gz" 2>/dev/null # macOS `stat`
        stat -c"%s" "${tmpFile}.gz" 2>/dev/null # GNU `stat`
    )

    # mv to target
    mv ${tmpFile}.gz ${target}.gz

    echo "${target}.gz ($((zippedSize / 1000)) kB) created successfully." "${@}"
    unset tempFile
    unset target
    unset zippedSize
    unset size
    unset cmd
    unset _exclude
}

# 获取文件或目录的大小
function size() {
    [[ -z "$1" ]] && {
        error "Usage: size <file/dir>"
        return $INVALID_ARGUMENTS
    }

    if du -b /dev/null >/dev/null 2>&1; then
        local arg=-sbh
    else
        local arg=-sh
    fi

    if [[ -n "$@" ]]; then
        du $arg -- "$@"
    else
        du $arg ./*
    fi
    unset arg
}

# 使用gzip压缩到base64
# encode <source> <target>
function gzip_encode() {
    [[ -z "$1" ]] && {
        error "Usage: gzip_encode <src file> <target file>"
        return $INVALID_ARGUMENTS
    }

    if [[ -s $1 ]]; then
        local tmp="$TEMP/$(rand 1 100)-$$"
        gzip -c -n "$1" | base64 >$tmp
        cp $tmp $2
        tip "$1 encode to $2"
        unset tmp
    fi
}

# 从base64文件还原
# decode <compress file> <target file>
function gzip_decode() {
        [[ -z "$1" ]] && {
        error "Usage: gzip_decode <compress file> <target file>"
        return $INVALID_ARGUMENTS
    }

    local tmpFile="$TEMP/$(rand 1 100)-$$"
    cat "$1" | base64 -d >$tmpFile
    gzip -c -d -n "$tmpFile" >$2
    tip "$1 decode to $2"
    unset tmpFile
}




########################################
# 描述: 路径下文件列表
# 参数: $1,目录，缺损:当前目录
# 返回: 
# 实例: ListFile [dir]
########################################
function list_file(){
    [[ -z "$1" ]] && {
        error "Usage: list_file [目录]"
        return $INVALID_ARGUMENTS
    }

    local f_path=$1
    local arr_file=()
    local i=1
    local file
    
    if [[ -z ${f_path} ]];then
        f_path=$(pwd)
    fi
    if [[ ! -d ${f_path} ]];then
        error "目录${f_path}不存在!"
        return 1
    fi
    msg "${BLUE}路径[${RESET}${YELLOW}${f_path}${RESET}${BLUE}]下文件列表：${RESET}"
    spec_char '\n'

    for file in $(find ${f_path} -type f)
    do
		msg "   [${RED}${i}${RESET}]   ${YELLOW}${file##*/}${RESET}"

        arr_file[${i}]=${file}
        let i++
    done
    
    spec_char '\n'
	msg "${BLUE}共识别到文件数:${RESET} ${RED}${#arr_file[@]}${RESET}"
	#echo ${arr_file}
	unset f_path
    unset arr_file
    unset i
    unset file
}
########################################
# 描述: 路径下目录列表
# 参数: $1,目录，缺损:当前目录
# 返回: 
# 实例: ListDir [dir]
########################################
function list_dir(){
    [[ -z "$1" ]] && {
        error "Usage: list_dir [目录]"
        return $INVALID_ARGUMENTS
    }

    local f_path=$1
    local arr_dir=()
    local i=1
    local dir
    
    if [[ -z ${f_path} ]];then
        f_path=$(pwd)
    fi
    msg "${BLUE}路径[${RESET}${YELLOW}${f_path}${RESET}${BLUE}]下目录列表：${RESET}"
    spec_char '\n'

    for dir in $(find ${f_path} -type d)
    do
        if [[ ${dir##*/} = ${f_path##*/} ]];then
            #去除路径目录本身
            continue
        else
		    msg "   [${RED}${i}${RESET}]   ${YELLOW}${dir##*/}${RESET}"

            arr_dir[${i}]=${dir}
            let i++
        fi
    done
    
    spec_char '\n'
	msg "${BLUE}共识别到目录数:${RESET} ${RED}${#arr_dir[@]}${RESET}"
	#echo ${arr_dir}
	unset dir
	unset f_path
    unset arr_dir
    unset i
}

########################################
# 描述: 比较文件，并保存结果及状态
# 参数: $1 文件1，$2 文件2，$3 保存结果
# 返回: 0，相同;1，不同
# 实例: compare_file "file1" "file2" "file3"
########################################
function compare_file(){
    [[ -z "$1" ]] && {
        error "Usage: compare_file <文件> <文件> <保存结果文件>"
        return $INVALID_ARGUMENTS
    }

    local file1=$1
    local file2=$2
    local diff_result=$(is_file $3)
    local status
    if [[ -s ${file1} ]] && [[ -s ${file2} ]];then
        eval "diff ${file1} ${file2} >${diff_result} && status=$?"
    fi
    echo ${status}
    unset file1
    unset file2
    unset diff_result
    unset status
}

########################################
# 描述: 删除文件
# 参数: $1 文件
# 返回: 
# 实例: del_file "file"
########################################
function del_file(){
    [[ -z "$1" ]] && {
        error "Usage: del_file <文件>"
        return $INVALID_ARGUMENTS
    }

    local file=$1
    if [[ -s ${file} ]];then
        eval "rm -vf ${file}"
    fi
    unset file
}

########################################
# 描述: 删除目录内$2天以前的文件
# 参数: $1 文件路径 $2,天数
# 返回: 
# 实例: clear_file "dir" 30
########################################
function clear_file(){
    [[ -z "$1" ]] && {
        error "Usage: clear_file <目录> <天数>"
        return $INVALID_ARGUMENTS
    }

    local f_dir=$1
    local n=$2
    eval "find ${f_dir} -type f -mtime +${n} |xargs rm -s"
    unset f_dir
    unset n
}

########################################
# 描述: 根据路径提取目录名称
# 参数: $1,目录路径字符串
# 返回: 最下级目录名称
# 实例: #get_dir_name '/root/aidroot/web'
## web
########################################
function get_dir_name(){
    [[ -z "$1" ]] && {
        error "Usage: get_dir_name <路径>"
        return $INVALID_ARGUMENTS
    }

    local d_path=$1
    if [[ -n ${d_path} ]];then
        echo ${d_path##*/}
    fi
    unset d_path
}

########################################
# 描述: 根据文件路径提取目录
# 参数: $1,文件完整路径
# 返回: 文件目录
# 实例: GetFileName '/root/aidroot/web/test.png'
## /root/aidroot/web
########################################
function get_file_dir(){
    [[ -z "$1" ]] && {
        error "Usage: get_file_dir <路径>"
        return $INVALID_ARGUMENTS
    }

    local f_path=$1
    if [[ -n ${f_path} ]];then
        echo ${f_path%/*}
    fi
    unset f_path
}

########################################
# 描述: 根据路径提取文件名称
# 参数: $1,文件完整路径
# 返回: 文件名称(含后缀)
# 实例: GetFileName '/root/aidroot/web/test.png'
## test.png
########################################
function get_file_name(){
    [[ -z "$1" ]] && {
        error "Usage: get_file_name <路径>"
        return $INVALID_ARGUMENTS
    }

    local f_path=$1
    if [[ -n ${f_path} ]];then
        echo ${f_path##*/}
    fi
    unset f_path
}

########################################
# 描述: 根据目录生成文件名称
# 参数: $1,目录路径字符串 [$2],文件后缀，缺失为"log"
# 返回: 文件名称
# 实例1: GetLogName "/home/web" "jpg"
## web.jpg
# 实例2: GetLogName "/home/web" 
## web.log
########################################
function get_log_name(){
    [[ -z "$1" ]] && {
        error "Usage: get_log_name <路径>"
        return $INVALID_ARGUMENTS
    }

    local d_dir=$1
    local l_suffix=$2
    local l_name
    if [[ -z ${l_suffix} ]];then
        l_suffix="log"
    fi
    l_name="${d_dir##*/}.${l_suffix}" 
    echo ${l_name}
    unset d_dir
    unset l_suffix
    unset l_name
}

########################################
# 描述: 取得目录下所有文件内容的md5值
# 参数: $1,检测的文目路径 $2,md5值保存的文件路径
# 返回: 
# 实例: dir_file_md5 "/home/web" "/root/log"
########################################
#
function md5_dir(){
    [[ -z "$1" ]] && {
        error "Usage: md5_dir <目标目录> <保存目录>"
        return $INVALID_ARGUMENTS
    }

    local chech_dir=$1
    local log_path=$2
    local i
    if [[ -n ${check_dir} ]] && [[ -n ${log_path} ]] ;then
        #参数不为空
        if [[ -d ${check_dir} ]];then
            for i in ${check_dir[@]}
            do
                eval "find ${i} -type f | xargs md5sum  > ${log_path}"
            done
        fi
    fi
    unset chech_dir
    unset log_path
    unset i
}

########################################
# 描述: 获取文件的md5
# 参数: $1,文件
# 返回: 文件的md5位
# 实例: GetFileMd5 "file"
########################################
function md5_file(){
    [[ -z "$1" ]] && {
        error "Usage: md5_file <目标文件>"
        return $INVALID_ARGUMENTS
    }

    local file=$1
    local md5=0
    if [[ -s ${file} ]];then
        md5=$(md5sum ${file} 2>/dev/null)
    fi
    echo ${md5}
    unset file
    unset md5
}

########################################
# 描述: 获取文件的上次修改时间
# 参数: $1,文件
# 返回: 最近修改时间(格式:Y-M-D/H:M:S)
# 实例: get_file_modify_time "file"
########################################
function get_file_modify_time(){
    [[ -z "$1" ]] && {
        error "Usage: get_file_modify_time <目标文件> "
        return $INVALID_ARGUMENTS
    }

    local file=$1
    local m_time
    local formart_date=""
    if [[ -s ${file} ]];then
        m_time=$(stat -c %Y ${file})
        formart_date=$(date '+%Y-%m-%d/%H:%M:%S' -d @$m_time)
    fi
    echo ${formart_date}
    unset file
    unset m_time
    unset formart_date
}

########################################
# 描述: 获取文件的大小
# 参数: $1,文件
# 返回: bytes
# 实例: GetFileSize "file"
########################################
function get_file_size(){
    [[ -z "$1" ]] && {
        error "Usage: get_file_size <目标文件> "
        return $INVALID_ARGUMENTS
    }

    local file=$1
    local size=0
    if [[ -s ${file} ]];then
        size=$(stat -c %s ${file})
    fi
    echo ${size}
    unset file
    unset size
}

########################################
# 描述: 获取文件的所有者
# 参数: $1,文件
# 返回: 所有者
# 实例: GetFileUser "file"
########################################
function get_file_user(){
    [[ -z "$1" ]] && {
        error "Usage: get_file_user <目标文件> "
        return $INVALID_ARGUMENTS
    }

    local file=$1
    local user=0
    if [[ -s ${file} ]];then
        user=$(stat -c %U ${file})
    fi
    echo ${user}
    unset file
    unset user
}

########################################
# 描述: 获取文件的权限
# 参数: $1,文件
# 返回: 权限数字
# 实例: GetFileRight "file"
## 600
########################################
function get_file_right(){
    [[ -z "$1" ]] && {
        error "Usage: get_file_right <目标文件> "
        return $INVALID_ARGUMENTS
    }

    local file=$1
    local right=0
    if [[ -s ${file} ]];then
        right=$(stat -c %a ${file})
    fi
    echo ${right}
    unset file
    unset right
}

########################################
# 描述: 重命名文件
# 参数: $1,文件  $2,标记字符串
# 返回: 文件完整路径
# 实例: file_rename "xxxx.xxx" "yyy"
## yyyy.xxx
########################################
function file_rename(){
    [[ -z "$1" ]] && {
        error "Usage: file_rename <old-sile> <new-sile-name>"
        return $INVALID_ARGUMENTS
    }
    local file=$1
    local f_name=$2
    if [[ ! -s ${file} ]];then
        error "无效文件: ${file}"
        return $INVALID_ARGUMENTS
    fi
    local f_path=${file%/*}
    [[ ${f_path} = ${file} ]] && f_path=" " || f_path="${f_path}/"
    local f_suff=${file##*.}
    local temp="${f_path}${f_name}.${f_suff}"
    chmod 777 ${file}
    eval "$cmd ${file} ${temp}"
    unset f_path f_name f_suff temp file
    
}

########################################
# 描述: 备份文件
# 参数: $1 文件
# 返回: 
# 实例: backup_file "file" [file]
########################################
function backup_file(){
    [[ -z "$1" ]] && {
        error "Usage: backup_file <目标文件> [新文件]"
        return $INVALID_ARGUMENTS
    }

    local file=$1
    local file2=$2
    local f_suffix
    local f_name
    local f_dir
    local file3
    if [[ -s ${file} ]];then
        #扩展名
        f_suffix=$(FileSuffix ${file})
        #文件名
        f_name=${$(basename ${file})%.*}
        #文件目录
        f_dir=${file%/*}
        if [[ -z ${file2} ]];then
            #组合
            file3="${f_dir}/${f_name}_$(date +%F__%T).${f_suffix}"
        else
            file3=$(is_file ${file2})
        fi
        eval "mv -vf ${file} ${file3}"
    fi
    unset file
    unset file2
    unset f_suffix
    unset f_name
    unset f_dir
    unset file3
}

#批量修改文件扩展名
function change_ext(){
    [[ -z "$1" ]] && {
        error "Usage: change_ext <文件后缀> "
        return $INVALID_ARGUMENTS
    }

    for i in  "ls *.$1"
    do
        mv  $i ${i%.*}.$2
    done
}

__file_init__































